/**
	License:		Artistic license 2.0 (see license.txt, also available online at http://www.perlfoundation.org/artistic_license_2_0)
	Authors:		OrbitalLab (http://www.orbitallab.ru/moonglide/), 2007-2009

	File:			openglShaderGLSL.d
	Description:	opengl shader interface for GLSL programs
	Date:			22.12.2007 by Jurec
*/

module moonglide.renderer.opengl.OpenGLShaderGLSL;

import
	moonglide.util.MoonUtil,
	tango.stdc.stringz,
	moonglide.renderer.IRendererRes,

	moonglide.renderer.opengl.GL,
	moonglide.renderer.opengl.OpenGLRenderer,
	moonglide.renderer.opengl.OpenGLShaderWork,

	moonglide.resources.resobjects.hilevel.ResShaderComposite;


//=============================================================
/// COpenGLShaderGLSLProv - opengl GLSL shader stuff
//=============================================================
class COpenGLShaderGLSLProvider : IShaderProvider
{
	protected:
		uint _GlConst(in ubyte ShT)
		{
			return (ShT == sh_Vertex) ? GL_VERTEX_SHADER_ARB : GL_FRAGMENT_SHADER_ARB;
		}
	private:
		char[] _XGetInfoLog( uint handle )
		{
			int bLen, sLen;
			char[] infoLog;
			glGetObjectParameterivARB( handle, GL_OBJECT_INFO_LOG_LENGTH_ARB, &bLen);

			if( bLen > 1 )
			{
				infoLog.length = bLen;
				glGetInfoLogARB( handle, bLen, &sLen, infoLog.ptr );
				return infoLog;
			}

			return "";
		}


		uint _XLoadShader( char[][] src, uint stype )
		{
			char[] source;

			foreach( str; src )
				source ~= str ~ "\n";

			int len = source.length;

			uint hand = glCreateShaderObjectARB(stype);

			char* ppSource = source.ptr;

			glShaderSourceARB( hand, 1, &ppSource, &len );
			glCompileShaderARB( hand );

			int compiled;
			glGetObjectParameterivARB(hand, GL_OBJECT_COMPILE_STATUS_ARB, &compiled);

			if (compiled != GL_TRUE)
			{
				errorLog( "Shader program contains errors: "  );
				moonAssert( 0, _XGetInfoLog(hand), __FILE__, __LINE__ );

				glDeleteObjectARB(hand);
				return 0;
			}

			return hand;
		}

	public:
		bool Upload( inout CResShaderComposite sh )
		{
			bool res = true;
			foreach( inout Pr; sh.progs )
				if( Pr )
				{
					Pr.handle =  _XLoadShader( Pr.text , _GlConst(Pr.sType) );
					if( Pr.handle == 0 )
					{
						Pr.setNotLoaded;
						Pr = null;
						res = false;
					} else 	Pr.setLoaded;

				}
			return res;
		}


		bool Unload( inout CResShaderComposite sh )
		{
			foreach( inout Pr; sh.progs )
				if( Pr )
				{
					glDeleteObjectARB( Pr.handle );
					Pr.setNotLoaded;
				}

			glDeleteObjectARB( sh.handle );
			return true;
		}


		void Enable()	{}
		void Disable()	{}


		void Bind( inout CResShaderComposite sh )
		{
			glUseProgramObjectARB( sh.handle );
		}

		bool Link(inout CResShaderComposite sh)
		{
			sh.handle = glCreateProgramObjectARB();

			foreach( inout Pr; sh.progs )
				if( Pr )
					glAttachObjectARB(sh.handle, Pr.handle);

			int linked = GL_FALSE;
			glLinkProgramARB(sh.handle);
			glGetObjectParameterivARB(sh.handle, GL_OBJECT_LINK_STATUS_ARB, &linked);
			char[] log = _XGetInfoLog(sh.handle);

			if( linked != GL_TRUE )
			{
		//		GetGLError(__FILE__,__LINE__);
				errorLog( "Shader program " ~ sh.id() ~ " contains errors: " ~ log );
				return false;
			}

			return true;
		}

		void Unbind( inout CResShaderComposite sh )
		{
			glUseProgramObjectARB(0);
		}


		bool Analyze( inout CResShaderComposite sh )
		{
// TODO (Jura#1#): uniform

			debug
			{
				debugLog.trace(" -----" ~ sh.id ~ "----- ");
/*
				foreach( loc; sh.locals )
					debugLog.trace( moonglide.resources.resobjects.ResBasic.layout(
						"{} {} {}", loc.name, loc.num, loc.isStatic ));
*/
				debugLog.trace(" ---------- ");
			}

			return true;
		}


		void SetLocal( inout SShaderLoc lo, ubyte shaderType )
		{
			char* na = toStringz( lo.name );
			glUseProgramObjectARB( shaderType );
			int loc = glGetUniformLocationARB( shaderType, na );

	///		if( lo.name == "blur_offset" )
     //           debugLog.trace("blur_offset binded " ~ intToStr(lo.size) ~ " fl:"~ floatToStr(lo.getFData.x)~" y: " ~floatToStr(lo.getFData.y)~" z: " ~floatToStr(lo.getFData.z) );

			if( loc == -1 )
				moonAssert( 0, "cannot get local uniform <"~ lo.name ~">", __FILE__, __LINE__ );



			if( lo.floatUse )
			{
				switch( lo.size )
				{
					case 1:	glUniform1fvARB( loc, 1, lo.getFData.ptr );
							break;
					case 2:	glUniform2fvARB( loc, 1, lo.getFData.ptr );
							break;
					case 3:	glUniform3fvARB( loc, 1, lo.getFData.ptr );
							break;
					case 4:	glUniform4fvARB( loc, 1, lo.getFData.ptr );
							break;
					default:
						moonAssert( 0, "default switch in SetLocal called!", __FILE__, __LINE__ );
						break;
				}
			} else
			{
				switch( lo.size )
				{
					case 1:	glUniform1ivARB( loc, 1, lo.getIData.ptr );
							break;
					case 2:	glUniform2ivARB( loc, 1, lo.getIData.ptr );
							break;
					case 3:	glUniform3ivARB( loc, 1, lo.getIData.ptr );
							break;
					case 4:	glUniform4ivARB( loc, 1, lo.getIData.ptr );
							break;
					default:
						moonAssert( 0, "default switch in SetLocal called!", __FILE__, __LINE__ );
						break;
				}
			}
			lo.binded = true;
		}


		void SetEnv( inout SShaderLoc lo, ubyte shaderType )
		{
		}
}
